using Box2DSharp.Collision.Collider;
using Box2DSharp.Collision.Shapes;
using Box2DSharp.Common;
using Box2DSharp.Dynamics;
using Box2DSharp.Dynamics.Contacts;
using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Testbed.Abstractions;
using Unity.VisualScripting;
using UnityEngine;
using cframework.lockstep;

namespace Testbed.TestCases
{
    [TestCase("Examples", "CannonWorld")]
    public class CannonWorld : TestBase, IContactListener
    {
        public string uid;

        public Dictionary<string, Body> userBodies = new Dictionary<string, Body>();
        public Dictionary<string, FP> userStickjoyAngles = new Dictionary<string, FP>();
        public Dictionary<string, bool> userBtnIsPressed = new Dictionary<string, bool>();

        FP cannonScale = 11;
        FP bodyRadius = 1;

        BodyDef bodyDef;
        FixtureDef fixtureDef;
        GameObject fishPrefab;


        public CannonWorld()
        {
            World.Gravity = new FVector2(0, 0);
            World.SetContactListener(this);


            fishPrefab = Resources.Load<GameObject>("cannon");

            
            bodyDef = new BodyDef {BodyType = BodyType.DynamicBody};
            bodyDef.Position.Set(0, 4f);


            {
                var bodyShape = new CircleShape();
                bodyShape.Radius = bodyRadius;

                fixtureDef = new FixtureDef
                {
                    Shape = bodyShape,
                    Density = 1.0f,
                    Friction = 0.3f,
                    Filter = new Filter() { CategoryBits = 1, MaskBits = 3 },
                };
            }


        }
        
        public void CreateBody(string uid)
        {
            if(userBodies.ContainsKey(uid))
            {
                return;
            }


            userBtnIsPressed[uid] = false;

            var body = World.CreateBody(bodyDef);
            body.CreateFixture(fixtureDef);


            

            var fish = GameObject.Instantiate(fishPrefab);
            body.UserData = fish;


            fish.transform.localScale = Vector3.one * (float)cannonScale;

            var fishPos = body.GetPosition();
            fish.transform.localPosition = (new Vector3((float)fishPos.X, (float)fishPos.Y, 0));
            fish.transform.localRotation = Quaternion.Euler(0, 0, (float)(body.GetAngle() * FMath.Rad2Deg));

            userBodies.Add(uid, body);
        }

        public void SetBodyVelocity(string uid, FVector2 velocity)
        {
            userBodies[uid].SetLinearVelocity(velocity);
        }


        public void UpdateBodyAngleVelocity(FP clientTime)
        {
            foreach (var entry in userBodies)
            {
                var uid = entry.Key;
                var angle = userStickjoyAngles.ContainsKey(uid)? userStickjoyAngles[uid] : -1;
                var body = userBodies[uid];

                if (angle < 0)
                {
                    SetBodyVelocity(uid, FVector2.Zero);
                    body.SetAngularVelocity(0);
                }
                else
                {
                    Rotation r = new Rotation(angle * FMath.Deg2Rad);
                    var velocity = MathUtils.MulT(r, FVector2.Right) * 10;
                    velocity.Y *= -1;
                    SetBodyVelocity(uid, velocity);



                    try
                    {
                        var bodyAngle = body.GetAngle() * FMath.Rad2Deg;
                        var destAngle = FVector2Ex.Angle(FVector2.Right, velocity);
                        if (velocity.Y < 0)
                        {
                            destAngle *= -1;
                        }

                        var diffAngle = destAngle - bodyAngle;

                        diffAngle -= (int)(diffAngle / 360) * 360;

                        if (FMath.Abs(diffAngle) > 180)
                        {
                            diffAngle -= FMath.Sign(diffAngle) * 360;
                        }


                        body.SetAngularVelocity(diffAngle * 0.9f * (1 / 0.1f) * FMath.Deg2Rad);
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogException(e);
                    }
                }
            }
        }

        protected override void PostStep()
        {
            UpdateBodyAngleVelocity(LockStepWorld.current.time);
        }


        public void UpdateTransforms()
        {
            var w = this.World;
            for (var node = w.BodyList.First; node != null; node = node.Next)
            {
                var b = node.Value;
                var xf = b.GetTransform();
                var isEnabled = b.IsEnabled;
                var isAwake = b.IsAwake;

                if (b.UserData is GameObject)
                {
                    var gameObj = b.UserData as GameObject;

                    var pos = b.GetPosition();
                    gameObj.transform.localPosition = new Vector3((float)pos.X, (float)pos.Y, 0);
                    gameObj.transform.localRotation = Quaternion.Euler(0, 0, (float)b.GetAngle() * Mathf.Rad2Deg);
                }
            }
        }
    }
}